package com.linux4fun.api;

import com.linux4fun.pojo.Person;

import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.*;


public class StreamApi {
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>();
        persons.add(new Person(1, "kyle", "peng", 25, Date.from(LocalDate.of(1993,9,3)
                .atStartOfDay().toInstant(ZoneOffset.UTC))));
        persons.add(new Person(2, "kai", "peng", 28, Date.from(LocalDate.of(1994,2,3)
                .atStartOfDay().toInstant(ZoneOffset.UTC))));
        persons.add(new Person(2, "akai", "peng", 28, Date.from(LocalDate.of(1991,5,3)
                .atStartOfDay().toInstant(ZoneOffset.UTC))));
        System.out.println(persons.toString());
        // 使用sorted方法对数组进行排序，reversed方法可以将顺序反转。
        persons = persons.stream().sorted(Comparator.comparing(Person::getBirthday).reversed()).collect(Collectors.toList());
        System.out.println(persons.toString());
        // =====================================================================
        // 流的使用示范
        List<String> myList = Arrays.asList("a1","b1","c2","d1","c5");
        myList
                .stream()
                .filter(s -> s.startsWith("c"))
                .map(String::toUpperCase)
                .sorted()
                .forEach(System.out::println);
        // 流操作不是中间的就是终端的，中间操作回返回一个stream对象，因此我们可以继续在后面链接其他的中间操作，而不用使用分号结束。
        // 终端操作要么是无返回值，或者返回一个非流的结果。上面的代码中，filter,map和sorted属于中间操作，而foreach属于终端操作。
        // 上面代码中的流式操作也被叫做操作流水线。
        // 对于函数而言，他不会修改流的底层数据源。
        // 当操作的执行是确定的时候，函数是无状态的，也就是说同样的代码被执行多次的结果都必定是一致的。否则，每次运行的结果都不一致，这样的函数被认为是
        // 有状态的。（从代码层面来说，流中使用到的lambda表达式不应该依赖于任何可变变量或者来自外部的可能在执行期间会发生变化的状态，一般来说，这应该
        // 被作为一种代码规范来遵守）


        // 除了可以使用集合类自带的stream方法创建流，java 8还提供了各种基本类型对于Stream的封装类。
        // 另外，也可以直接使用Stream的静态方法构造一个流对象
        IntStream.range(1,5)
                .forEach(System.out::println);
        DoubleStream.of(1,2,3,4)
                .forEach(System.out::println);
        Stream.of("a1","b1","c1")
                .findFirst()
                .ifPresent(System.out::println);
        // 流的中间操作有一个非常重要的特性就是懒加载。即如果没有终端操作，那么中间操作并不会立即执行。
        Stream.of("a1","b1","c1","d1")
                .filter(s -> {
                    System.out.println(s);
                    return true;
                });

        // 现在给上面的代码加上foreach的终端操作
        Stream.of("a1","b1","c1","d1")
                .filter(s -> {
                    System.out.println("filter: " + s);
                    return true;
                })
                .forEach(s -> System.out.println("foreach: " + s));

        // 使用collect方法构造复杂对象,统计对象list中的某个属性
        IntSummaryStatistics summaryStatistics = persons
                .stream()
                .collect(Collectors.summarizingInt(p -> p.getAge()));
        System.out.println(summaryStatistics.getMax());

        // 使用join方法合并对象属性
        String phrase = persons
                .stream()
                .filter(p -> p.getAge() > 18)
                .map(p -> p.getFirstName())
                .collect(Collectors.joining(" 和 ", "在中国，", " 已经是个成年人了！"));
        System.out.println(phrase);

        // 将对象属性和属性值转化为map对象，需要注意的是，map中的键必须是唯一的，因此，如果出现了两个对象有相同的属性值，那么需要自定义一个合并函数
        // 进行转化,下面的例子中使用了转换函数，将键值相等的属性值进行拼接。
        Map<Integer, String> map = persons
                .stream()
                .collect(Collectors.toMap(
                        p -> p.getAge(),
                        p -> p.getLastName(),
                        (name1, name2) -> name1 + ";" + name2
                ));
        System.out.println(map);
        
        // 自定义collector
        // 使用”|“作为间隔符连接所有人的名字，并将他们的名字转换成大写字符
        Collector<Person, StringJoiner, String> personNameCollector =
                Collector.of(
                        () -> new StringJoiner(" | "),
                        (j, p) -> j.add(p.getLastName().toUpperCase()),
                        (j1, j2) -> j1.merge(j2),
                        StringJoiner::toString
                );
        String names  =persons.stream().collect(personNameCollector);
        System.out.println(names);
    }

}
